/** @file    SocketTcpQt.cpp
 *  @author  Alessandro Polo
 *  @version $Id: SocketTcpQt.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the wosh::network::qt::SocketTcpQt class.
 * The header for this class can be found in SocketTcpQt.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/network/qt/SocketTcpQt.h>
 #include <framework/network/qt/ServerTcpQt.h>

 #include <core/ObjectAllocator.h>
 #include <framework/network/NetworkUtilities.h>
 #include <core/Utilities.h>


#ifdef _SocketTcpQt_DEBUG_ENABLED
# define LOG( LEVEL, FORMAT, ... ) if ( Log.getLevel() >= LEVEL ) { Log(LEVEL, FORMAT, __VA_ARGS__); }
#else
# define LOG( LEVEL, FORMAT, ... ) ;
#endif


using namespace std;
namespace wosh {
 namespace network {
  namespace qt {

  WOSH_REGISTER(wosh::network::qt::SocketTcpQt, "wosh::network::SocketTcp", 1.0, _static_SocketTcpQt )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

SocketTcpQt::SocketTcpQt()
	: QTcpSocket(), wosh::network::SocketTcp() {
#ifdef _SocketTcpQt_DEBUG_ENABLED
	Log.setIndent( 5 );
	Log.setContext( "SocketTcpQt" );
	Log.setLevel( LOG_INFO );
#endif
	QObject::connect(this, SIGNAL(readyRead()), this, SLOT(readClient()));
	QObject::connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socket_error(QAbstractSocket::SocketError)));

 }

SocketTcpQt::~SocketTcpQt() {
	LOG(LOG_VERBOSE, ":~SocketTcpQt() Destroying.." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SocketTcpQt::connect( unsigned long timeout ) {
	if ( this->state() == QAbstractSocket::ConnectedState )
		return WRET_ERR_PARAM;
	QHostAddress address;
	address.setAddress( QString(this->address.c_str()) );
	this->connectToHost ( address, this->port );
	bool connected = waitForConnected(timeout);
	if ( !connected ) {
		LOG(LOG_CRITICAL, ":connect() FAILED [%s]", this->errorString().toLatin1().data() );
		return WRET_ERR_INTERNAL;
	 }
	return WRET_OK;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SocketTcpQt::disconnect( unsigned long timeout ) {
	this->disconnectFromHost();
	if ( this->state() != QAbstractSocket::UnconnectedState ) {
		bool disconnected = waitForDisconnected(timeout);
		if ( !disconnected ) {
			LOG(LOG_CRITICAL, ":connect() FAILED [%s]", this->errorString().toLatin1().data() );
			return WRET_ERR_INTERNAL;
		 }
	 }
	this->close();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int SocketTcpQt::sendDatagram( const char* data, int size ) {
	if ( data == NULL ) return -1;
	if ( size <= 0 ) size = strlen(data);
	LOG(LOG_DEBUG, ":sendDatagram() %d bytes..", size );
	size = write(data, size);
	this->flush();
	return size;
 }

void SocketTcpQt::readClient() {
	uint64 ba = (uint64)bytesAvailable();
	LOG(LOG_DEBUG, ":readClient() %"PRIu64" bytes..", ba );
	if ( this->listener != NULL )
		this->listener->tcpDataAvailable(ba, this);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool SocketTcpQt::waitData( unsigned long timeout ) {
	int msec = _GET_Abs((int)timeout);
	return waitForReadyRead(msec);
 }

bool SocketTcpQt::waitWritten( unsigned long timeout ) {
	return waitForBytesWritten(timeout);
 }

bool SocketTcpQt::waitResponse( char* data, int max_size, long& size, unsigned long timeout ) {
	size = 0;
	bool done = false;
	done = waitForReadyRead(timeout);
	if ( !done )
		return false;

	size = this->readData(data, max_size);
	///@bug here
	size = strlen(data);
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT SocketTcpQt::setSocket( int socket_descriptor ) {
	 bool done = QTcpSocket::setSocketDescriptor(socket_descriptor);
	 if ( !done ) {
		LOG(LOG_CRITICAL, ":setSocket(%d) : FAILED!", socket_descriptor );
		return WRET_ERR_INTERNAL;
	  }
	LOG(LOG_INFO, ":setSocket(%d) : Applied", socket_descriptor );

	QHostAddress add = peerAddress();
	this->address = std::string(add.toString().toAscii().data());
	this->port = peerPort();
	this->address_port = Utilities::format("%s:%d", this->address.c_str(), this->port);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string SocketTcpQt::getPeerAddressPort() const {
	return wosh::Utilities::format( "%s:%d", peerAddress().toString().toLatin1().data(), peerPort() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SocketTcpQt::socket_error(QAbstractSocket::SocketError socketError) {
	switch (socketError) {
		case QAbstractSocket::RemoteHostClosedError:
			LOG(LOG_WARNING, ":socket_error() Socket Error 'Remote Host closed Connection'!" );
			if ( this->listener != NULL )
				this->listener->tcpSocketError(_SocketTcp_ERROR_RemoteHostClosedError, "Remote Host closed Connection", this);
			break;
		case QAbstractSocket::HostNotFoundError:
			LOG(LOG_CRITICAL, ":socket_error() Socket Error 'Host not Found' [%s:%d]", this->address.c_str(), this->port );
			if ( this->listener != NULL )
				this->listener->tcpSocketError(_SocketTcp_ERROR_HostNotFoundError, "Host not Found", this);
			break;
		case QAbstractSocket::ConnectionRefusedError:
			LOG(LOG_CRITICAL, ":socket_error() Socket Error 'Connection REFUSED'!" );
			if ( this->listener != NULL )
				this->listener->tcpSocketError(_SocketTcp_ERROR_ConnectionRefusedError, "Connection REFUSED", this);
			break;
		default:
			LOG(LOG_WARNING, ":socket_error() Socket Error '%s'!", this->errorString().toLatin1().data() );
			if ( this->listener != NULL )
				this->listener->tcpSocketError(0, this->errorString().toLatin1().data(), this);
	}
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }; // namespace qt
 }; // namespace network
}; // namespace wosh
